﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Compiler;
using Microsoft.Xna.Framework.Content;

namespace AfterlifeLib.Levels
{
    public enum LayerType
    {
        Tile,
        Collision,
        Light,
    }

    /// <summary>
    /// Stores layer data and methods for modifying the data.
    /// </summary>
    public class Layer
    {
        const int MAX_RECURSIVE_FILLS = 1000;

        int recursiveFillCounter = 0;

        int[,] values;
        protected int[,] Values
        {
            get { return values; }
            set { values = value; }
        }

        public int this[int x, int y]
        {
            get { return values[y, x]; }
            set { values[y, x] = value; }
        }

        public int Width
        {
            get { return values.GetLength(1); }
        }

        public int Height
        {
            get { return values.GetLength(0); }
        }

        protected Layer() 
        {
            
        }

        public Layer(int[,] values)
        {
            Values = values;
        }

        public Layer(int width, int height)
        {
            if (width <= 0 || height <= 0)
                throw new IndexOutOfRangeException("The dimensions must be greater then 0.");

            Values = new int[height, width];
        }

        public void WriteXML(XmlWriter writer, int layerNumber, LayerType type)
        {
            writer.WriteStartElement(
                string.Format(type == LayerType.Tile ? 
                string.Format("TileLayer{0}", layerNumber) : 
                string.Format("CollisionLayer{0}", layerNumber), 
                layerNumber));
            writer.WriteAttributeString("Width", Width.ToString());
            writer.WriteAttributeString("Height", Height.ToString());
            for (int y = 0; y < Height; y++)
            {
                string row = "";

                for (int x = 0; x < Width; x++)
                {
                    row += Values[y, x].ToString() + " ";
                }

                writer.WriteElementString("Row", row);
            }
            writer.WriteEndElement();
        }

        public void ReadXML(XmlReader reader)
        {
            int width = int.Parse(reader.GetAttribute(0));
            int height = int.Parse(reader.GetAttribute(1));

            int[,] values = new int[height, width];

            reader.Read();

            for (int y = 0; y < height; y++)
            {
                reader.Read();

                string row = reader.ReadElementContentAsString();
                string[] rowContent = row.Split(' ');

                for (int x = 0; x < width; x++)
                {
                    int value = int.Parse(rowContent[x]);
                    values[y, x] = value;
                }
            }

            Values = values;
        }

        public void WriteBinary(ContentWriter output)
        {
            output.Write(Width);
            output.Write(Height);

            for (int x = 0; x < Width; x++)
                for (int y = 0; y < Height; y++)
                    output.Write(this[x, y]);
        }

        public void ReadBinary(ContentReader input)
        {
            int width = input.ReadInt32();
            int height = input.ReadInt32();
            Values = new int[height, width];

            for (int x = 0; x < width; x++)
                for (int y = 0; y < height; y++)
                    this[x, y] = input.ReadInt32();
        }

        public void TotalFill(int newIndex)
        {
            for (int x = 0; x < Width; x++)
                for (int y = 0; y < Height; y++)
                    this[x, y] = newIndex;
        }

        public void RecursiveFill(int x, int y, int newIndex)
        {
            recursiveFillCounter = 0;

            RecursiveFillMethod(x, y, newIndex);
        }

        private void RecursiveFillMethod(int x, int y, int newValue)
        {
            int oldValue = this[x, y];

            if (oldValue == newValue || recursiveFillCounter >= MAX_RECURSIVE_FILLS)
                return;

            recursiveFillCounter++;

            this[x, y] = newValue;

            //Left
            if (x > 0 && Values[y, x - 1] == oldValue)
                RecursiveFillMethod(x - 1, y, newValue);

            //Right
            if (x < Width - 1 && Values[y, x + 1] == oldValue)
                RecursiveFillMethod(x + 1, y, newValue);

            //Up
            if (y > 0 && Values[y - 1, x] == oldValue)
                RecursiveFillMethod(x, y - 1, newValue);

            //Down
            if (y < Height - 1 && Values[y + 1, x] == oldValue)
                RecursiveFillMethod(x, y + 1, newValue);
        }

        public void SetValues(int[,] values)
        {
            Values = values;
        }

        public void ReplaceValues(int oldValue, int newValue)
        {
            for (int x = 0; x < Width; x++)
                for (int y = 0; y < Height; y++)
                    if (this[x, y] == oldValue)
                        this[x, y] = newValue;
        }

        /// <summary>
        /// Resizes the Values array.
        /// </summary>
        /// <param name="newWidth">The new width of the array.</param>
        /// <param name="newHeight">The new height of the array.</param>
        /// <param name="newValue">The value that should be used for filling new empty spots.</param>
        public void Resize(int newWidth, int newHeight, int newValue)
        {
            if (newWidth <= 0 || newHeight <= 0)
                throw new IndexOutOfRangeException("The dimensions must be greater then 0.");

            int[,] newValues = new int[newHeight, newWidth];

            for (int x = 0; x < newWidth; x++)
                for (int y = 0; y < newHeight; y++)
                    if (x >= Width || y >= Height)
                        newValues[y, x] = newValue;
                    else
                        newValues[y, x] = Values[y, x];

            Values = newValues;
        }

        public void ResizeOffset(int offsetX, int offsetY, int newValue)
        {
            Resize(Width + offsetX, Height + offsetY, newValue);
        }
    }
}
